package org.orbis.search.rpc.impl;

import java.io.IOException;
import org.msgpack.MessagePackObject;
import org.msgpack.object.ArrayType;
import org.msgpack.object.IntegerType;
import org.msgpack.object.NilType;
import org.msgpack.object.RawType;
import org.msgpack.rpc.Request;
import org.msgpack.rpc.Server;
import org.msgpack.rpc.loop.EventLoop;
import org.orbis.communicate.MainService;
import org.orbis.logging.LogParser;
import org.orbis.search.SearchPackage;
import org.orbis.structure.ContentInfo;

public class SearchService{
	// Work without executor
	public boolean stopFlag = false;
	public volatile int threadCounter = 0;
	private MainService mains = null;
	private ContentInfo contentInfo;
	private int port;
	private Server server;

	public SearchService(MainService mains, int port){
		this.mains = mains;
		this.port = port;
		this.contentInfo = mains.contentInfo();
	}

	public class SearchRPCHandler{
		/** @param req Arguments with shapes {@link ArrayType}, details as follows.<br>
		 *            [0]: {@link String}, Query<br>
		 *            [1]: int, Result start<br>
		 *            [2]: int, Result length<br>
		 *            [3]: boolean, intersect results if true, or join all results<br>
		 *            [4]: int, Target number<br>
		 *            [5]-[...]: {@link String}, Target field name<br>
		 * @throws Exception */
		public void search(Request req) throws Exception{
			MessagePackObject obj = req.getArguments();
			MessagePackObject[] args = obj.asArray();

			if(args.length < 9
				&& (!args[0].isRawType() || !args[1].isIntegerType() || !args[2].isIntegerType()
					|| !args[3].isBooleanType() || !args[4].isIntegerType() || !args[5].isIntegerType()
					|| !args[6].isIntegerType() || !args[7].isIntegerType() || !args[8].isIntegerType())){
				LogParser.severe("Please post appropriate query.");
				req.sendError(RawType.create("Please post appropriate query."));
			}

			int targetNum = args[4].asInt();
			if(targetNum == 0){
				req.sendResult(RawType.create("No field selected. Not found."));
				return;
			}

			int binaryNum = args[5].asInt();
			int intNum = args[6].asInt();
			int longNum = args[7].asInt();
			int floatNum = args[8].asInt();
			int subtotal = 9;

			// Input //////////////////
			SearchPackage pkg =
				SearchPackage.createPackage(args[0].asString(), args[1].asInt(), args[2].asInt(),
					args[3].asBoolean(), contentInfo);
			
			for(int i = 0; i < binaryNum; i++){
				if(args[subtotal + i].isBooleanType()) pkg.setType(i, args[subtotal + i].asBoolean());
				else if(args[subtotal + i].isIntegerType())
					pkg.setType(i, args[subtotal + i].asInt() == 1? true: false);
			}
			
			subtotal += binaryNum;
			String fieldName;
			int val1, val2;
			int type;
			for(int i = 0; i < intNum; i++){
				fieldName = args[subtotal + 4 * i].asString();
				val1 = args[subtotal + 4 * i + 1].asInt();
				if(args[subtotal + 4 * i + 2].isIntegerType())
					val2 = args[subtotal + 4 * i + 2].asInt();
				else val2 = 0;
				type = args[subtotal + 4 * i + 3].asInt();
				
			}
		}

		public void argument(Request req){
			int contentVal = mains.contentInfo().contentsNum();
			int binaryVal = mains.contentInfo().binaryFlagNum();
			int multiVal = mains.contentInfo().integerFlagNum();

			MessagePackObject[] objs = new MessagePackObject[3 + contentVal];
			objs[0] = IntegerType.create(6 + binaryVal + (multiVal * 3));
			objs[1] = IntegerType.create(binaryVal);
			objs[2] = IntegerType.create(multiVal);
			for(int i = 0; i < contentVal; i++)
				objs[3 + i] = RawType.create(mains.contentInfo().contentName(i));

			req.sendResult(ArrayType.create(objs));
		}

		public void stop(Request req){
			this.notifyAll();
			req.sendResult(NilType.create());
		}
	}

	public void initService(){
		EventLoop eventLoop = EventLoop.defaultEventLoop();
		try{
			server = new Server(eventLoop);
			server.serve(new SearchRPCHandler());
			server.listen(port);
		}catch(IOException e){}
	}

	public void stopService(){
		server.close();
	}
}
